#include "ui_skin.h"
#include "sprite_bin.h"
#include "script/scripthelper.h"

#include "graphics/graphics_util.h"
#include "num/rand.h"

#include "util/timer.h"		    // Needs to come in early
#include "util/mmgr.h"		    // Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"		    // logging
#include "util/profiler.h"

#include <math.h>

using namespace ui;
using namespace script;
using namespace LuaPlus;


UISkin&	UISkin::Instance()
{
	static UISkin	Inst;
	return Inst;
}

void	UISkin::Load(const std::string& File)
{
	m_Elements.ClearSet();

	// load 
	LuaPlus::LuaState*	pScript = ScriptHelper::CreateLuaState();
	if (!ScriptHelper::DoFile(*pScript, File))
	{
		_LOG(MSG_ERROR, "Unable to load UI theme " << File << "!");
		 LuaState::Destroy( pScript );
		return;
	} else
	{
		LuaPlus::LuaObject	Skin = ScriptHelper::GetObj(*pScript, "Skin");
		if (Skin.IsTable())
		{
			for (unsigned int i=0; i<(unsigned int)Skin.GetTableCount(); i++)
			{
				LuaPlus::LuaObject	SkinElement = Skin.GetByIndex(i+1);


				UISkinData*	pData = new UISkinData;
				pData->Name	=ScriptHelper::GetTableString(SkinElement, "Name");

				pData->ClampSize	=ScriptHelper::GetTableBool(SkinElement, "ClampSize", true);
				pData->ImageA[UISkinData::IMAGE_OFF].LoadImg(SkinElement.GetByName("MouseOff"));
				pData->ImageA[UISkinData::IMAGE_OVER].LoadImg(SkinElement.GetByName("MouseOver"));
				pData->ImageA[UISkinData::IMAGE_CLICK].LoadImg(SkinElement.GetByName("MouseClick"));

				m_Elements.AddElement(pData->Name, pData);
			}
		}
	}
	LuaState::Destroy( pScript );


}
UISkinElement*	UISkin::CreateElement(const std::string& Name)
{
	UISkinElement* pElement = new UISkinElement;
	UISkinData*	pData = m_Elements.GetElement(Name);
	pElement->Name = Name;
	pElement->Timer = num::RAND32::Instance().RandFloat(0, 10);
	pElement->pData = pData;
	return pElement;
}

void	UISkin::ForEach(FindSkinCB cb, void* Data)
{
	m_Elements.ForEach(cb, Data);
}

math::Vec2	UISkinElement::AdjustSize(const math::Vec2& OrigSize)
{
	if (!pData) return OrigSize;
	if (!pData->ClampSize) return OrigSize;
	UISkinImage*	pImg = &pData->ImageA[UISkinData::IMAGE_OFF];
	if (OrigSize.Magnitude() < 0.001f)	// empty
	{
		/// return the actual size
		if (pImg->SingleImage)
		{
			return pImg->TileA[UISkinImage::ST_IMAGE].m_Size;
		} else
		{
			return pImg->GetTileSize() * 3.0f;
		}
	} else
	{
		if (pImg->SingleImage)
		{
			return OrigSize;
		} else
		{
			// give a bit of leeway to floating point errors
			const float dx = 0.5f * GraphicsUtil::InvW;
			const float dy = 0.5f * GraphicsUtil::InvH;

			math::Vec2	TileSize = pImg->GetTileSize();
			int	width = (int)ceil( (OrigSize.x / TileSize.x) - dx );
			int	height = (int)ceil( (OrigSize.y / TileSize.y) - dy );
			return math::Vec2(TileSize.x*width, TileSize.y*height);
		}
	}
	return OrigSize;
}

void		UISkinElement::Render(UISkinData::IMAGE_STATES State, const math::Vec2& Pos, const math::Vec2& Size, COLOR c)
{
	PROFILE_BLOCK("UISkinElement");

	if (!pData) return;
	if (!pData->ImageA[State].IsValid())
	{
		if (State==UISkinData::IMAGE_OFF) return;	// never going to be valid
		Render(UISkinData::IMAGE_OFF, Pos, Size, c);	// try with a mouse-over image instead
		return;
	}
	// do we make the assumption that all tiles are uni-sized?
	UISkinImage*	pImg = &pData->ImageA[State];
	math::Vec2	TileSize = pImg->GetTileSize();

	if (InvTileSize.x <= 0.0f)	InvTileSize.x = 1.0f / TileSize.x;
	if (InvTileSize.y <= 0.0f)	InvTileSize.y = 1.0f / TileSize.y;
	
	// do we resize single images?
	if (pImg->SingleImage)
	{	
		// use tilesize instead?
		pImg->TileA[UISkinImage::ST_IMAGE].Setup(Pos, Size, 0, 1, c);
		pImg->TileA[UISkinImage::ST_IMAGE].Render();
	} else
	{
		// there's only MAX 9 pieces
		//	but could be less, if the size does hold a complete tile size
		float		cx = Size.x - (TileSize.x*2);	// size of center component
		float		cy = Size.y - (TileSize.y*2);

		for (int x=0; x<3; x++)
		for (int y=0; y<3; y++)
		{
			if (x == 1 && cx < 0) continue;	// no center tile
			if (y == 1 && cy < 0) continue; // no center tile
			if (x == 2 && Size.x <=TileSize.x) continue;	// not big enough to do more than single left stripe
			if (y == 2 && Size.y <=TileSize.y) continue;	// not big enough to do more than single top stripe

			const int	Index = x + (y*3);
			math::Vec2	TL = Pos + math::Vec2(TileSize.x * x, TileSize.y * y);
			if (x==2) TL.x = Pos.x + TileSize.x + cx;
			if (y==2) TL.y = Pos.y + TileSize.y + cy;
			pImg->TileA[Index].Setup(TL, TileSize, 0, 1, c);
			if (x==1) pImg->TileA[Index].m_Size.x =cx;
			if (y==1) pImg->TileA[Index].m_Size.y =	cy;
			pImg->TileA[Index].Render();
		}
	}
}


math::Vec2		UISkinImage::GetTileSize()
{
	if (SingleImage) return TileA[ST_IMAGE].m_Size;
	if (!TileA[ST_TL].IsValid()) return math::Vec2(0,0);
	return TileA[ST_TL].m_Size;
}

void			UISkinImage::LoadTile(SKIN_TILES Tile, const std::string& File)
{
	if (File.empty())
	{
		TileA[Tile].Invalidate();
		return;
	}
	TileA[Tile].Create(File);
	TileA[Tile].GetScreenSize(TileA[Tile].m_Size);
}

void			UISkinImage::LoadImg(LuaPlus::LuaObject Obj)
{
	if (Obj.IsNil()) return;
	if (Obj.IsString())
	{
		LoadTile(ST_IMAGE,	Obj.ToString());
		SingleImage = true;
		return;
	}
	if (Obj.IsTable())
	{
		SingleImage = false;
		
		LoadTile(ST_TL,	ScriptHelper::GetTableString(Obj, "TL"));
		LoadTile(ST_TC,	ScriptHelper::GetTableString(Obj, "TC"));
		LoadTile(ST_TR,	ScriptHelper::GetTableString(Obj, "TR"));
		LoadTile(ST_CL,	ScriptHelper::GetTableString(Obj, "CL"));
		LoadTile(ST_CC,	ScriptHelper::GetTableString(Obj, "CC"));
		LoadTile(ST_CR,	ScriptHelper::GetTableString(Obj, "CR"));
		LoadTile(ST_BL,	ScriptHelper::GetTableString(Obj, "BL"));
		LoadTile(ST_BC,	ScriptHelper::GetTableString(Obj, "BC"));
		LoadTile(ST_BR,	ScriptHelper::GetTableString(Obj, "BR"));
		
	}

}
